"""
认证模块 - 安全工具集
"""
from typing import Optional

import bcrypt
from fastapi import Depends, Request
from jwt import PyJWTError

from axiom_boot.auth.models import Principal
from axiom_boot.auth.services.token_service import TokenService
from axiom_boot.core.exceptions import Unauthorized
from axiom_boot.logging.setup import get_logger

logger = get_logger(__name__)

# 定义一个常量来存储 token 在 request.state 中的键
HTTP_BEARER_TOKEN = "_http_bearer_token"


def get_password_hash(password: str) -> str:
    """
    使用 bcrypt 对密码进行哈希处理。
    """
    hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt())
    return hashed_password.decode('utf-8')


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    验证明文密码是否与 bcrypt 哈希值匹配。
    """
    try:
        return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8'))
    except ValueError:
        # 如果 hashed_password 格式不正确, checkpw 会抛出 ValueError
        return False


async def get_token_payload(request: Request) -> dict:
    """
    一个 FastAPI 依赖项，用于从请求头中解析、验证并返回 JWT 令牌的 payload。
    如果令牌无效或缺失，将抛出 Unauthorized 异常。
    """
    token_service: TokenService = await request.app.context.get(TokenService)
    
    auth_header = request.headers.get("Authorization")
    if not auth_header or not auth_header.startswith("Bearer "):
        raise Unauthorized("未提供有效的认证头")

    token = auth_header.split(" ")[1]
    
    # 将 token 存入 request state 以便在同一请求中复用
    setattr(request.state, HTTP_BEARER_TOKEN, token)

    try:
        payload = await token_service.decode_token(token)
        return payload
    except PyJWTError as e:
        logger.warning(f"JWT 解码失败: {e}", exc_info=True)
        raise Unauthorized("令牌无效或已过期")


async def get_current_principal(request: Request, payload: dict = Depends(get_token_payload)) -> Principal:
    """
    一个 FastAPI 依赖项，用于从已验证的 token payload 中构建并返回 Principal 对象。
    这是在受保护的端点中获取当前用户身份的主要方式。
    """
    principal = Principal.model_validate(payload)

    # 关键修复：将 principal 和其权限列表存入 request.state
    # 这样，后续的权限检查中间件或依赖项就可以直接访问
    setattr(request.state, "principal", principal)
    setattr(request.state, "permissions", principal.permissions)
    
    return principal


async def get_current_principal_or_none(request: Request) -> Optional[Principal]:
    """
    一个 FastAPI 依赖项，尝试获取当前认证主体，但如果认证失败或未提供凭据，则返回 None 而不是抛出异常。
    这主要用于像 /authorize 这样需要知道用户是否已登录，但本身不强制要求登录的端点。
    """
    try:
        # 复用 get_token_payload 和 get_current_principal 的逻辑
        payload = await get_token_payload(request)
        # 关键修复：必须使用关键字参数来调用，以避免将 payload 错误地赋给 request 参数
        principal = await get_current_principal(request=request, payload=payload)
        return principal
    except Unauthorized:
        return None
